import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';

import 'package:crypto/crypto.dart';
import 'package:flutter/material.dart';
import 'package:private_journey/services/clipboard_history_service.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'package:flutter/services.dart';

import 'widget_for_reader.dart';

Future<T?> readValue<T extends Object>(
    DataReader reader, ValueFormat<T> format) {
  final c = Completer<T?>();
  ReadProgress? progress;
  final timer = Timer(const Duration(milliseconds: 200), () {
    progress?.cancel();
    if (!c.isCompleted) {
      c.complete(null);
    }
  });

  progress = reader.getValue<T>(format, (value) {
    timer.cancel();
    if (!c.isCompleted) {
      c.complete(value);
    }
  }, onError: (e) {
    timer.cancel();
    if (!c.isCompleted) {
      c.completeError(e);
    }
  });

  if (progress == null) {
    timer.cancel();
    c.complete(null);
  }

  return c.future;
}

Future<Uint8List?> readFile(DataReader reader, FileFormat format) {
  final c = Completer<Uint8List?>();
  ReadProgress? progress;
  final timer = Timer(const Duration(milliseconds: 400), () {
    progress?.cancel();
    if (!c.isCompleted) {
      c.complete(null);
    }
  });

  progress = reader.getFile(format, (file) async {
    try {
      // Do not cancel timer here, readAll can hang
      final data = await file.readAll();
      timer.cancel();
      if (!c.isCompleted) {
        c.complete(data);
      }
    } catch (e) {
      timer.cancel();
      if (!c.isCompleted) {
        c.completeError(e);
      }
    }
  }, onError: (e) {
    timer.cancel();
    if (!c.isCompleted) {
      c.completeError(e);
    }
  });

  if (progress == null) {
    timer.cancel();
    c.complete(null);
  }

  return c.future;
}

class Clipboardpage extends StatefulWidget {
  const Clipboardpage({super.key});

  @override
  State<Clipboardpage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<Clipboardpage> {
  Timer? _timer;
  final ClipboardHistoryRepository _historyRepository =
      InMemoryClipboardHistoryRepository();
  List<ClipboardHistoryEntry> _history = [];

  @override
  void initState() {
    super.initState();
    // Listen for paste events to trigger a sync.
    ClipboardEvents.instance?.registerPasteEventListener(_onPasteEvent);
    // Periodically check the clipboard for changes.
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      _syncClipboard();
    });
    // Initial load.
    _loadHistory();
  }

  @override
  void dispose() {
    _timer?.cancel();
    ClipboardEvents.instance?.unregisterPasteEventListener(_onPasteEvent);
    super.dispose();
  }

  void _onPasteEvent(ClipboardReadEvent event) {
    _syncClipboard();
  }

  Future<void> _loadHistory() async {
    final history = await _historyRepository.getAll();
    if (mounted) {
      setState(() {
        _history = history;
      });
    }
  }

  Future<String> _calculateClipboardMd5(ClipboardReader reader) async {
    final bytes = <int>[];
    for (final item in reader.items) {
      if (item.canProvide(Formats.plainText)) {
        final text = await readValue(item, Formats.plainText);
        if (text != null) {
          bytes.addAll(utf8.encode(text));
        }
      } else if (item.canProvide(Formats.png)) {
        final image = await readFile(item, Formats.png);
        if (image != null) {
          bytes.addAll(image);
        }
      } else if (item.canProvide(Formats.jpeg)) {
        final image = await readFile(item, Formats.jpeg);
        if (image != null) {
          bytes.addAll(image);
        }
      } else if (item.canProvide(Formats.gif)) {
        final image = await readFile(item, Formats.gif);
        if (image != null) {
          bytes.addAll(image);
        }
      } else if (item.canProvide(Formats.uri)) {
        final uri = await readValue(item, Formats.uri);
        if (uri != null) {
          bytes.addAll(utf8.encode(uri.uri.toString()));
        }
      }
    }
    return md5.convert(bytes).toString();
  }

  void _syncClipboard() async {
    final clipboard = SystemClipboard.instance;
    if (clipboard == null) return;

    final reader = await clipboard.read();
    if (reader.items.isEmpty) return;

    final currentMd5 = await _calculateClipboardMd5(reader);
    if (currentMd5 == "d41d8cd98f00b204e9800998ecf8427e") return; // Empty hash

    if (_history.any((entry) => entry.md5 == currentMd5)) {
      return;
    }

    final readers =
        await Future.wait(reader.items.map((e) => ReaderInfo.fromReader(e)));

    if (readers.isNotEmpty) {
      await _historyRepository.add(
        ClipboardHistoryEntry(md5: currentMd5, item: readers),
      );
      await _loadHistory();
    }
  }

  void _clearHistory() async {
    await _historyRepository.clear();
    await _loadHistory();
  }

  @override
  Widget build(BuildContext context) {
    return HomeLayout(
      buttons: [
        OutlinedButton(
          onPressed: _syncClipboard,
          style: OutlinedButton.styleFrom(
            backgroundColor: Colors.blue.shade600,
            foregroundColor: Colors.white,
          ),
          child: const Text('Sync'),
        ),
        OutlinedButton(
          onPressed: _clearHistory,
          style: OutlinedButton.styleFrom(
            backgroundColor: Colors.red.shade600,
            foregroundColor: Colors.white,
          ),
          child: const Text('Clear History'),
        ),
      ],
      mainContent: _history.isEmpty
          ? const Center(child: Text('Clipboard history is empty.'))
          : SingleChildScrollView(
              padding: const EdgeInsets.all(16),
              child: Wrap(
                spacing: 16,
                runSpacing: 16,
                children: _history.reversed
                    .map((entry) => ClipboardHistoryCard(entry: entry))
                    .toList(),
              ),
            ),
    );
  }
}

class ClipboardHistoryCard extends StatefulWidget {
  final ClipboardHistoryEntry entry;

  const ClipboardHistoryCard({super.key, required this.entry});

  @override
  State<ClipboardHistoryCard> createState() => _ClipboardHistoryCardState();
}

class _ClipboardHistoryCardState extends State<ClipboardHistoryCard> {
  List<Widget> _widgets = [];
  bool _loading = true;
  bool _isHovering = false;

  bool _initialized = false;

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    if (!_initialized) {
      _initialized = true;
      buildWidgetsForReaders(context, widget.entry.item, (widgets) {
        if (mounted) {
          setState(() {
            _widgets = widgets;
            _loading = false;
          });
        }
      });
    }
  }

  @override
  void initState() {
    super.initState();
  }

  Future<void> _copyToClipboard() async {
    final clipboard = SystemClipboard.instance;
    if (clipboard == null) {
      return;
    }
    Clipboard.setData(ClipboardData(text: 'Hello, World!'));
    // final writer = DataWriter();
    //
    // for (final info in widget.entry.item) {
    //   final reader = info.reader;
    //   if (reader.canProvide(Formats.plainText)) {
    //     final value = await readValue(reader, Formats.plainText);
    //     if (value != null) {
    //       writer.addText(value);
    //     }
    //   }
    //   if (reader.canProvide(Formats.png)) {
    //     final value = await readFile(reader, Formats.png);
    //     if (value != null) {
    //       writer.add(Formats.png(value));
    //     }
    //   }
    //   if (reader.canProvide(Formats.jpeg)) {
    //     final value = await readFile(reader, Formats.jpeg);
    //     if (value != null) {
    //       writer.add(Formats.jpeg(value));
    //     }
    //   }
    //   if (reader.canProvide(Formats.gif)) {
    //     final value = await readFile(reader, Formats.gif);
    //     if (value != null) {
    //       writer.add(Formats.gif(value));
    //     }
    //   }
    //   if (reader.canProvide(Formats.uri)) {
    //     final value = await readValue(reader, Formats.uri);
    //     if (value != null) {
    //       writer.add(Formats.uri(value));
    //     }
    //   }
    // }
    //
    // if (writer.items.isNotEmpty) {
    //   await clipboard.write(writer);
    //   if (!mounted) return;
    //   ScaffoldMessenger.of(context).showSnackBar(
    //     const SnackBar(
    //       content: Text('Copied to clipboard!'),
    //       duration: Duration(seconds: 1),
    //     ),
    //   );
    // }
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onEnter: (_) => setState(() => _isHovering = true),
      onExit: (_) => setState(() => _isHovering = false),
      child: Card(
        elevation: 2,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
        child: Stack(
          children: [
            Padding(
              padding: const EdgeInsets.all(12.0),
              child: _loading
                  ? const Center(child: CircularProgressIndicator())
                  : Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: _widgets,
                    ),
            ),
            if (_isHovering)
              Positioned(
                top: 0,
                right: 0,
                child: IconButton(
                  icon: const Icon(Icons.copy_all_outlined),
                  onPressed: _copyToClipboard,
                  tooltip: 'Copy to clipboard',
                ),
              ),
          ],
        ),
      ),
    );
  }
}

class HomeLayout extends StatelessWidget {
  const HomeLayout({
    super.key,
    required this.mainContent,
    required this.buttons,
  });

  final Widget mainContent;
  final List<Widget> buttons;

  @override
  Widget build(BuildContext context) {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: [
        SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: IntrinsicWidth(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: buttons
                    .map((e) => Padding(
                          padding: const EdgeInsets.only(bottom: 10),
                          child: e,
                        ))
                    .toList(),
              ),
            ),
          ),
        ),
        VerticalDivider(
          color: Colors.blueGrey.shade100,
          thickness: 1,
          width: 1,
        ),
        Expanded(
          child: mainContent,
        ),
      ],
    );
  }
}

